﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Threading;

namespace Nina
{
    /// <summary>
    /// A socket for client instances
    /// </summary>
    public class NinaSocketConnector : NinaService
    {
        private NinaSession m_session;

        /// <summary>
        /// Constructor
        /// </summary>
        public NinaSocketConnector()
            : base()
        {
            m_session = null;
        }

        /// <summary>
        /// Connects to a host on a specific port
        /// </summary>
        /// <param name="host">The host address (domain name or IP)</param>
        /// <param name="port">The port number</param>
        /// <returns>A NinaSession object on success, null on failure</returns>
        public NinaSession Connect(string host, int port)
        {
            IPHostEntry hostEntry = null;
            IPAddress[] addresses = null;

            try
            {
                if (Regex.IsMatch(host, @"[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*"))
                {
                    /* Direct IP address */
                    addresses = new IPAddress[1];
                    addresses[0] = IPAddress.Parse(host);
                }
                else
                {
                    /* Find all IP addresses associated with the host */
                    hostEntry = Dns.GetHostEntry(host);
                    addresses = hostEntry.AddressList;
                }
            }
            catch
            {
                return null;
            }

            /* For each IP address associated with the host, attempt to connect */
            foreach (IPAddress address in addresses)
            {
                NinaSession result = Connect(address, port);
                if (result != null)
                {
                    m_session = result;
                    return m_session;
                }
            }

            /* Return null on failure */
            return null;
        }

        /// <summary>
        /// Connects to a host on a specific port
        /// </summary>
        /// <param name="host">The host IP address</param>
        /// <param name="port">The port number</param>
        /// <returns>A NinaSession object on success, null on failure</returns>
        public NinaSession Connect(IPAddress host, int port)
        {
            IPEndPoint endPoint = new IPEndPoint(host, port);
            Socket socket = new Socket(endPoint.AddressFamily,
                SocketType.Stream, ProtocolType.Tcp);
            try
            {
                /* Attempt to connect */
                socket.Connect(endPoint);
                if (socket.Connected)
                {
                    /* Successful connection, build a NinaSession object */
                    m_session = new NinaSession(socket);

                    /* Notify protocol handler and return the session */
                    m_protocolHandler.SessionOpened(m_session);

                    /* Queue session for processing */
                    ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessSession), m_session);

                    return m_session;
                }
            }
            catch { }

            /* Return null on failure */
            return null;
        }

        /// <summary>
        /// Disconnects the underlying NinaSession object
        /// </summary>
        public void Disconnect()
        {
            if (m_session != null)
            {
                m_session.Close();
            }
        }
    }
}
